/**
 * Created by chenyong on 2015/7/16.
 */

var passport = require('passport-strategy'),
    url = require('url'),
    crypto = require('crypto'),
    util = require('util'),
    request = require('request'),
    querystring = require('querystring'),
    TokenError = require('./error/tokenerror'),
    AuthorizationError = require('./error/authorizationerror'),
    InternalOAuthError = require('./error/internaloautherror');



var originalURL = function(req, options) {
    options = options || {};
    var app = req.app;
    if (app && app.get && app.get('trust proxy')) {
        options.proxy = true;
    }
    var trustProxy = options.proxy;

    var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase()
        , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0])
        , host = (trustProxy && req.headers['x-forwarded-host']) || req.headers.host
        , protocol = tls ? 'https' : 'http'
        , path = req.url || '';
    return protocol + '://' + host + path;
};

function WechatStrategy(options, verify){
    if (typeof options == 'function') {
        verify = options;
        options = undefined;
    }
    options = options || {};

    if (!verify) { throw new TypeError('WechatStrategy 需要一个用于验证的回掉函数'); }
    if (!options.clientID) { throw new TypeError('WechatStrategy 需要填写ClientID选项'); }
    if (!options.clientSecret) { throw new TypeError('WechatStrategy 需要填写ClientSecerct选项'); }

    this.appId = options.clientID;
    this.appSecret = options.clientSecret;

    passport.Strategy.call(this);

    this.name = 'wechat';
    this._verify = verify;

    this._callbackURL = options.callbackURL;
    this._scope = options.scope;
    this._scopeSeparator = options.scopeSeparator || ' ';
    this._state = options.state;
    this._key = options.sessionKey || ('oauth2:api.weixin.qq.com');
    this._trustProxy = options.proxy;
}

util.inherits(WechatStrategy, passport.Strategy);

/**
 * 验证请求
 */
WechatStrategy.prototype.authenticate = function(req, options){
    options = options || {};
    var self = this;

    var params = {};
    params.grant_type = 'authorization_code';
    params.redirect_uri = callbackURL;

    if (req.query && req.query.error) {
        if (req.query.error == 'access_denied') {
            return this.fail({ message: req.query.error_description });
        } else {
            return this.error(new AuthorizationError(req.query.error_description, req.query.error, req.query.error_uri));
        }
    }

    var callbackURL = options.callbackURL || this._callbackURL;
    if (callbackURL) {
        var parsed = url.parse(callbackURL);
        if (!parsed.protocol) {
            // 如果回调地址是相对地址、则转变为绝对地址
            callbackURL = url.resolve(originalURL(req, { proxy: this._trustProxy }), callbackURL);
        }
    }

    //获得授权码之后开始读取用户信息
    if (req.query && req.query.code){
        var code = req.query.code;
        self.getAccessToken(code,function(err,token){
            if (err) { return self.error(self._createOAuthError('未能获得访问令牌', err)); }

            getUser(token.openid,token.access_token, function (err,profile) {
                if (err) { return self.error(err); }

                function verified(err, user, info) {
                    if (err) { return self.error(err); }
                    if (!user) { return self.fail(info); }
                    self.success(user, info);
                }

                try {

                    self._verify(token.access_token, token.refresh_token, profile, verified);
                } catch (ex) {
                    return self.error(ex);
                }
            });
        })
    }
    else{
        var scope = options.scope || this._scope;
        if (scope) {
            if (Array.isArray(scope)) { scope = scope.join(this._scopeSeparator); }
        }
        var state = options.state;
        if (this._state) {
            if (!req.session) { return this.error(new Error('WechatStrategy 需要 session 支持用户保存会话状态. 请添加 app.use(express.session(...))?')); }

            var key = this._key;
            state = randomuid(24);
            if (!req.session[key]) { req.session[key] = {}; }
            req.session[key].state = state;
        }

        var ua = req.useragent;
        var location = '';
        if(ua.isMobile)
        {
            location = this.getAuthorizeURL(this.appId,callbackURL,state,scope);
        }
        else
        {
            location = this.getAuthorizeURLForWebsite(this.appId,callbackURL,state,scope);
        }
        this.redirect(location);
    }

};

//随机字符串
var UIDCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
function randomuid(length)
{
    var bytes = crypto.pseudoRandomBytes(length);
    var chars, r, i;
    r = [];
    for (i = 0; i < bytes.length; i++) {
        r.push(UIDCHARS[bytes[i] % UIDCHARS.length]);
    }
    return r.join('');
}

/**
 * 创建一个 OAuth 异常.
 */
WechatStrategy.prototype._createOAuthError = function(message, err) {
    var e;
    if (err.statusCode && err.data) {
        try {
            e = this.parseErrorResponse(err.data, err.statusCode);
        } catch (_) {}
    }
    if (!e) { e = new InternalOAuthError(message, err); }
    return e;
};

WechatStrategy.prototype.parseErrorResponse = function(body, status) {
    var json = JSON.parse(body);
    if (json.error) {
        return new TokenError(json.error_description, json.error, json.error_uri);
    }
    return null;
};

/**
 * 从腾讯服务器获取access_token
 */
WechatStrategy.prototype.getAccessToken = function(code,callback){
    var url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='+ this.appId
        + '&secret=' + this.appSecret
        + '&code=' + code
        + '&grant_type=authorization_code';
    request({url:url,json:true},function(err,httpResponse,body){

        if (body.errcode) {
            err = new Error(body.errmsg);
            err.name = 'WeChatAPIError';
            err.code = body.errcode;
        }
        callback(err,body);
    });
};

/**
 * 读取用户信息
 */
var getUser = function (openid, accessToken, callback) {

    var info = {
        access_token: accessToken,
        openid: openid
    };
    var url = 'https://api.weixin.qq.com/sns/userinfo?' + querystring.stringify(info);

    request({url:url,json:true},function(err,httpResponse,body){
        if (body.errcode) {
            err = new Error(body.errmsg);
            err.name = 'WeChatAPIError';
            err.code = body.errcode;
        }
        callback(err,body);
    });
};




/**
 * 获取授权页面的URL地址
 * @param {String} redirect 授权后要跳转的地址
 * @param {String} state 开发者可提供的数据
 * @param {String} scope 作用范围，值为snsapi_userinfo和snsapi_base，前者用于弹出，后者用于跳转
 */
WechatStrategy.prototype.getAuthorizeURL = function (appid,redirect, state, scope) {
    var url = 'https://open.weixin.qq.com/connect/oauth2/authorize';
    var info = {
        appid: appid,
        redirect_uri: redirect,
        response_type: 'code',
        //scope: scope || 'snsapi_base',
        scope: scope || 'snsapi_login',

        state: state || ''
    };
    return url + '?' + querystring.stringify(info) + '#wechat_redirect';
};

/**
 * 获取授权页面的URL地址
 * @param {String} redirect 授权后要跳转的地址
 * @param {String} state 开发者可提供的数据
 * @param {String} scope 作用范围，值为snsapi_login，前者用于弹出，后者用于跳转
 */
WechatStrategy.prototype.getAuthorizeURLForWebsite = function (appid,redirect, state, scope) {
    var url = 'https://open.weixin.qq.com/connect/qrconnect';
    var info = {
        appid: appid,
        redirect_uri: redirect,
        response_type: 'code',
        scope: scope || 'snsapi_login',
        state: state || ''
    };
    return url + '?' + querystring.stringify(info) + '#wechat_redirect';
};

module.exports = WechatStrategy;